use expect_test::expect;

use super::{check_infer, check_infer_with_mismatches, check_types};

#[test]
fn infer_pattern() {
    check_infer(
        r#"
        fn test(x: &i32) {
            let y = x;
            let &z = x;
            let a = z;
            let (c, d) = (1, "hello");

            for (e, f) in some_iter {
                let g = e;
            }

            if let [val] = opt {
                let h = val;
            }

            let lambda = |a: u64, b, c: i32| { a + b; c };

            let ref ref_to_x = x;
            let mut mut_x = x;
            let ref mut mut_ref_to_x = x;
            let k = mut_ref_to_x;
        }
        "#,
        expect![[r#"
            8..9 'x': &i32
            17..368 '{     ...o_x; }': ()
            27..28 'y': &i32
            31..32 'x': &i32
            42..44 '&z': &i32
            43..44 'z': i32
            47..48 'x': &i32
            58..59 'a': i32
            62..63 'z': i32
            73..79 '(c, d)': (i32, &str)
            74..75 'c': i32
            77..78 'd': &str
            82..94 '(1, "hello")': (i32, &str)
            83..84 '1': i32
            86..93 '"hello"': &str
            101..151 'for (e...     }': ()
            105..111 '(e, f)': ({unknown}, {unknown})
            106..107 'e': {unknown}
            109..110 'f': {unknown}
            115..124 'some_iter': {unknown}
            125..151 '{     ...     }': ()
            139..140 'g': {unknown}
            143..144 'e': {unknown}
            157..204 'if let...     }': ()
            164..169 '[val]': [{unknown}]
            165..168 'val': {unknown}
            172..175 'opt': [{unknown}]
            176..204 '{     ...     }': ()
            190..191 'h': {unknown}
            194..197 'val': {unknown}
            214..220 'lambda': |u64, u64, i32| -> i32
            223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32
            224..225 'a': u64
            232..233 'b': u64
            235..236 'c': i32
            243..255 '{ a + b; c }': i32
            245..246 'a': u64
            245..250 'a + b': u64
            249..250 'b': u64
            252..253 'c': i32
            266..278 'ref ref_to_x': &&i32
            281..282 'x': &i32
            292..301 'mut mut_x': &i32
            304..305 'x': &i32
            315..335 'ref mu...f_to_x': &mut &i32
            338..339 'x': &i32
            349..350 'k': &mut &i32
            353..365 'mut_ref_to_x': &mut &i32
        "#]],
    );
}

#[test]
fn infer_literal_pattern() {
    check_infer_with_mismatches(
        r#"
        fn any<T>() -> T { loop {} }
        fn test(x: &i32) {
            if let "foo" = any() {}
            if let 1 = any() {}
            if let 1u32 = any() {}
            if let 1f32 = any() {}
            if let 1.0 = any() {}
            if let true = any() {}
        }
        "#,
        expect![[r#"
            17..28 '{ loop {} }': T
            19..26 'loop {}': !
            24..26 '{}': ()
            37..38 'x': &i32
            46..208 '{     ...) {} }': ()
            52..75 'if let...y() {}': ()
            59..64 '"foo"': &str
            59..64 '"foo"': &str
            67..70 'any': fn any<&str>() -> &str
            67..72 'any()': &str
            73..75 '{}': ()
            80..99 'if let...y() {}': ()
            87..88 '1': i32
            87..88 '1': i32
            91..94 'any': fn any<i32>() -> i32
            91..96 'any()': i32
            97..99 '{}': ()
            104..126 'if let...y() {}': ()
            111..115 '1u32': u32
            111..115 '1u32': u32
            118..121 'any': fn any<u32>() -> u32
            118..123 'any()': u32
            124..126 '{}': ()
            131..153 'if let...y() {}': ()
            138..142 '1f32': f32
            138..142 '1f32': f32
            145..148 'any': fn any<f32>() -> f32
            145..150 'any()': f32
            151..153 '{}': ()
            158..179 'if let...y() {}': ()
            165..168 '1.0': f64
            165..168 '1.0': f64
            171..174 'any': fn any<f64>() -> f64
            171..176 'any()': f64
            177..179 '{}': ()
            184..206 'if let...y() {}': ()
            191..195 'true': bool
            191..195 'true': bool
            198..201 'any': fn any<bool>() -> bool
            198..203 'any()': bool
            204..206 '{}': ()
        "#]],
    );
}

#[test]
fn infer_range_pattern() {
    check_infer_with_mismatches(
        r#"
        fn test(x: &i32) {
            if let 1..76 = 2u32 {}
            if let 1..=76 = 2u32 {}
        }
        "#,
        expect![[r#"
            8..9 'x': &i32
            17..75 '{     ...2 {} }': ()
            23..45 'if let...u32 {}': ()
            30..35 '1..76': u32
            38..42 '2u32': u32
            43..45 '{}': ()
            50..73 'if let...u32 {}': ()
            57..63 '1..=76': u32
            66..70 '2u32': u32
            71..73 '{}': ()
        "#]],
    );
}

#[test]
fn infer_pattern_match_ergonomics() {
    check_infer(
        r#"
        struct A<T>(T);

        fn test() {
            let A(n) = &A(1);
            let A(n) = &mut A(1);
        }
        "#,
        expect![[r#"
            27..78 '{     ...(1); }': ()
            37..41 'A(n)': A<i32>
            39..40 'n': &i32
            44..49 '&A(1)': &A<i32>
            45..46 'A': A<i32>(i32) -> A<i32>
            45..49 'A(1)': A<i32>
            47..48 '1': i32
            59..63 'A(n)': A<i32>
            61..62 'n': &mut i32
            66..75 '&mut A(1)': &mut A<i32>
            71..72 'A': A<i32>(i32) -> A<i32>
            71..75 'A(1)': A<i32>
            73..74 '1': i32
        "#]],
    );
}

#[test]
fn infer_pattern_match_ergonomics_ref() {
    cov_mark::check!(match_ergonomics_ref);
    check_infer(
        r#"
        fn test() {
            let v = &(1, &2);
            let (_, &w) = v;
        }
        "#,
        expect![[r#"
            10..56 '{     ...= v; }': ()
            20..21 'v': &(i32, &i32)
            24..32 '&(1, &2)': &(i32, &i32)
            25..32 '(1, &2)': (i32, &i32)
            26..27 '1': i32
            29..31 '&2': &i32
            30..31 '2': i32
            42..49 '(_, &w)': (i32, &i32)
            43..44 '_': i32
            46..48 '&w': &i32
            47..48 'w': i32
            52..53 'v': &(i32, &i32)
        "#]],
    );
}

#[test]
fn infer_pattern_match_slice() {
    check_infer(
        r#"
        fn test() {
            let slice: &[f64] = &[0.0];
            match slice {
                &[] => {},
                &[a] => {
                    a;
                },
                &[b, c] => {
                    b;
                    c;
                }
                _ => {}
            }
        }
        "#,
        expect![[r#"
            10..209 '{     ...   } }': ()
            20..25 'slice': &[f64]
            36..42 '&[0.0]': &[f64; 1]
            37..42 '[0.0]': [f64; 1]
            38..41 '0.0': f64
            48..207 'match ...     }': ()
            54..59 'slice': &[f64]
            70..73 '&[]': &[f64]
            71..73 '[]': [f64]
            77..79 '{}': ()
            89..93 '&[a]': &[f64]
            90..93 '[a]': [f64]
            91..92 'a': f64
            97..123 '{     ...     }': ()
            111..112 'a': f64
            133..140 '&[b, c]': &[f64]
            134..140 '[b, c]': [f64]
            135..136 'b': f64
            138..139 'c': f64
            144..185 '{     ...     }': ()
            158..159 'b': f64
            173..174 'c': f64
            194..195 '_': &[f64]
            199..201 '{}': ()
        "#]],
    );
}

#[test]
fn infer_pattern_match_string_literal() {
    check_infer_with_mismatches(
        r#"
        fn test() {
            let s: &str = "hello";
            match s {
                "hello" => {}
                _ => {}
            }
        }
        "#,
        expect![[r#"
            10..98 '{     ...   } }': ()
            20..21 's': &str
            30..37 '"hello"': &str
            43..96 'match ...     }': ()
            49..50 's': &str
            61..68 '"hello"': &str
            61..68 '"hello"': &str
            72..74 '{}': ()
            83..84 '_': &str
            88..90 '{}': ()
        "#]],
    );
}

#[test]
fn infer_pattern_match_or() {
    check_infer_with_mismatches(
        r#"
        fn test() {
            let s: &str = "hello";
            match s {
                "hello" | "world" => {}
                _ => {}
            }
        }
        "#,
        expect![[r#"
            10..108 '{     ...   } }': ()
            20..21 's': &str
            30..37 '"hello"': &str
            43..106 'match ...     }': ()
            49..50 's': &str
            61..68 '"hello"': &str
            61..68 '"hello"': &str
            61..78 '"hello...world"': &str
            71..78 '"world"': &str
            71..78 '"world"': &str
            82..84 '{}': ()
            93..94 '_': &str
            98..100 '{}': ()
        "#]],
    );
}

#[test]
fn infer_pattern_match_arr() {
    check_infer(
        r#"
        fn test() {
            let arr: [f64; 2] = [0.0, 1.0];
            match arr {
                [1.0, a] => {
                    a;
                },
                [b, c] => {
                    b;
                    c;
                }
            }
        }
        "#,
        expect![[r#"
            10..179 '{     ...   } }': ()
            20..23 'arr': [f64; 2]
            36..46 '[0.0, 1.0]': [f64; 2]
            37..40 '0.0': f64
            42..45 '1.0': f64
            52..177 'match ...     }': ()
            58..61 'arr': [f64; 2]
            72..80 '[1.0, a]': [f64; 2]
            73..76 '1.0': f64
            73..76 '1.0': f64
            78..79 'a': f64
            84..110 '{     ...     }': ()
            98..99 'a': f64
            120..126 '[b, c]': [f64; 2]
            121..122 'b': f64
            124..125 'c': f64
            130..171 '{     ...     }': ()
            144..145 'b': f64
            159..160 'c': f64
        "#]],
    );
}

#[test]
fn infer_adt_pattern() {
    check_infer(
        r#"
        enum E {
            A { x: usize },
            B
        }

        struct S(u32, E);

        fn test() {
            let e = E::A { x: 3 };

            let S(y, z) = foo;
            let E::A { x: new_var } = e;

            match e {
                E::A { x } => x,
                E::B if foo => 1,
                E::B => 10,
            };

            let ref d @ E::A { .. } = e;
            d;
        }
        "#,
        expect![[r#"
            67..288 '{     ...  d; }': ()
            77..78 'e': E
            81..94 'E::A { x: 3 }': E
            91..92 '3': usize
            105..112 'S(y, z)': S
            107..108 'y': u32
            110..111 'z': E
            115..118 'foo': S
            128..147 'E::A {..._var }': E
            138..145 'new_var': usize
            150..151 'e': E
            158..244 'match ...     }': usize
            164..165 'e': E
            176..186 'E::A { x }': E
            183..184 'x': usize
            190..191 'x': usize
            201..205 'E::B': E
            209..212 'foo': bool
            216..217 '1': usize
            227..231 'E::B': E
            235..237 '10': usize
            255..274 'ref d ...{ .. }': &E
            263..274 'E::A { .. }': E
            277..278 'e': E
            284..285 'd': &E
        "#]],
    );
}

#[test]
fn enum_variant_through_self_in_pattern() {
    check_infer(
        r#"
        enum E {
            A { x: usize },
            B(usize),
            C
        }

        impl E {
            fn test() {
                match (loop {}) {
                    Self::A { x } => { x; },
                    Self::B(x) => { x; },
                    Self::C => {},
                };
            }
        }
        "#,
        expect![[r#"
            75..217 '{     ...     }': ()
            85..210 'match ...     }': ()
            92..99 'loop {}': !
            97..99 '{}': ()
            115..128 'Self::A { x }': E
            125..126 'x': usize
            132..138 '{ x; }': ()
            134..135 'x': usize
            152..162 'Self::B(x)': E
            160..161 'x': usize
            166..172 '{ x; }': ()
            168..169 'x': usize
            186..193 'Self::C': E
            197..199 '{}': ()
        "#]],
    );
}

#[test]
fn infer_generics_in_patterns() {
    check_infer(
        r#"
        struct A<T> {
            x: T,
        }

        enum Option<T> {
            Some(T),
            None,
        }

        fn test(a1: A<u32>, o: Option<u64>) {
            let A { x: x2 } = a1;
            let A::<i64> { x: x3 } = A { x: 1 };
            match o {
                Option::Some(t) => t,
                _ => 1,
            };
        }
        "#,
        expect![[r#"
            78..80 'a1': A<u32>
            90..91 'o': Option<u64>
            106..243 '{     ...  }; }': ()
            116..127 'A { x: x2 }': A<u32>
            123..125 'x2': u32
            130..132 'a1': A<u32>
            142..160 'A::<i6...: x3 }': A<i64>
            156..158 'x3': i64
            163..173 'A { x: 1 }': A<i64>
            170..171 '1': i64
            179..240 'match ...     }': u64
            185..186 'o': Option<u64>
            197..212 'Option::Some(t)': Option<u64>
            210..211 't': u64
            216..217 't': u64
            227..228 '_': Option<u64>
            232..233 '1': u64
        "#]],
    );
}

#[test]
fn infer_const_pattern() {
    check_infer_with_mismatches(
        r#"
        enum Option<T> { None }
        use Option::None;
        struct Foo;
        const Bar: usize = 1;

        fn test() {
            let a: Option<u32> = None;
            let b: Option<i64> = match a {
                None => None,
            };
            let _: () = match () { Foo => Foo }; // Expected mismatch
            let _: () = match () { Bar => Bar }; // Expected mismatch
        }
        "#,
        expect![[r#"
            73..74 '1': usize
            87..309 '{     ...atch }': ()
            97..98 'a': Option<u32>
            114..118 'None': Option<u32>
            128..129 'b': Option<i64>
            145..182 'match ...     }': Option<i64>
            151..152 'a': Option<u32>
            163..167 'None': Option<u32>
            171..175 'None': Option<i64>
            192..193 '_': ()
            200..223 'match ... Foo }': Foo
            206..208 '()': ()
            211..214 'Foo': Foo
            218..221 'Foo': Foo
            254..255 '_': ()
            262..285 'match ... Bar }': usize
            268..270 '()': ()
            273..276 'Bar': usize
            280..283 'Bar': usize
            200..223: expected (), got Foo
            262..285: expected (), got usize
        "#]],
    );
}

#[test]
fn infer_guard() {
    check_infer(
        r#"
struct S;
impl S { fn foo(&self) -> bool { false } }

fn main() {
    match S {
        s if s.foo() => (),
    }
}
        "#,
        expect![[r#"
            27..31 'self': &S
            41..50 '{ false }': bool
            43..48 'false': bool
            64..115 '{     ...   } }': ()
            70..113 'match ...     }': ()
            76..77 'S': S
            88..89 's': S
            93..94 's': S
            93..100 's.foo()': bool
            104..106 '()': ()
    "#]],
    )
}

#[test]
fn match_ergonomics_in_closure_params() {
    check_infer(
        r#"
        #[lang = "fn_once"]
        trait FnOnce<Args> {
            type Output;
        }

        fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} }

        fn test() {
            foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics
            foo(&(1, "a"), |(x, y)| x);
        }
        "#,
        expect![[r#"
            93..94 't': T
            99..100 'f': F
            110..121 '{ loop {} }': U
            112..119 'loop {}': !
            117..119 '{}': ()
            133..232 '{     ... x); }': ()
            139..142 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
            139..166 'foo(&(...y)| x)': i32
            143..152 '&(1, "a")': &(i32, &str)
            144..152 '(1, "a")': (i32, &str)
            145..146 '1': i32
            148..151 '"a"': &str
            154..165 '|&(x, y)| x': |&(i32, &str)| -> i32
            155..162 '&(x, y)': &(i32, &str)
            156..162 '(x, y)': (i32, &str)
            157..158 'x': i32
            160..161 'y': &str
            164..165 'x': i32
            203..206 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
            203..229 'foo(&(...y)| x)': &i32
            207..216 '&(1, "a")': &(i32, &str)
            208..216 '(1, "a")': (i32, &str)
            209..210 '1': i32
            212..215 '"a"': &str
            218..228 '|(x, y)| x': |&(i32, &str)| -> &i32
            219..225 '(x, y)': (i32, &str)
            220..221 'x': &i32
            223..224 'y': &&str
            227..228 'x': &i32
        "#]],
    );
}

#[test]
fn slice_tail_pattern() {
    check_infer(
        r#"
        fn foo(params: &[i32]) {
            match params {
                [head, tail @ ..] => {
                }
            }
        }
        "#,
        expect![[r#"
            7..13 'params': &[i32]
            23..92 '{     ...   } }': ()
            29..90 'match ...     }': ()
            35..41 'params': &[i32]
            52..69 '[head,... @ ..]': [i32]
            53..57 'head': &i32
            59..68 'tail @ ..': &[i32]
            66..68 '..': [i32]
            73..84 '{         }': ()
        "#]],
    );
}

#[test]
fn box_pattern() {
    check_infer(
        r#"
        pub struct Global;
        #[lang = "owned_box"]
        pub struct Box<T, A = Global>(T);

        fn foo(params: Box<i32>) {
            match params {
                box integer => {}
            }
        }
        "#,
        expect![[r#"
            83..89 'params': Box<i32, Global>
            101..155 '{     ...   } }': ()
            107..153 'match ...     }': ()
            113..119 'params': Box<i32, Global>
            130..141 'box integer': Box<i32, Global>
            134..141 'integer': i32
            145..147 '{}': ()
        "#]],
    );
    check_infer(
        r#"
        #[lang = "owned_box"]
        pub struct Box<T>(T);

        fn foo(params: Box<i32>) {
            match params {
                box integer => {}
            }
        }
        "#,
        expect![[r#"
            52..58 'params': Box<i32>
            70..124 '{     ...   } }': ()
            76..122 'match ...     }': ()
            82..88 'params': Box<i32>
            99..110 'box integer': Box<i32>
            103..110 'integer': i32
            114..116 '{}': ()
        "#]],
    );
}

#[test]
fn tuple_ellipsis_pattern() {
    check_infer(
        r#"
fn foo(tuple: (u8, i16, f32)) {
    match tuple {
        (.., b, c) => {},
        (a, .., c) => {},
        (a, b, ..) => {},
        (a, b) => {/*too short*/}
        (a, b, c, d) => {/*too long*/}
        _ => {}
    }
}"#,
        expect![[r#"
            7..12 'tuple': (u8, i16, f32)
            30..224 '{     ...   } }': ()
            36..222 'match ...     }': ()
            42..47 'tuple': (u8, i16, f32)
            58..68 '(.., b, c)': (u8, i16, f32)
            63..64 'b': i16
            66..67 'c': f32
            72..74 '{}': ()
            84..94 '(a, .., c)': (u8, i16, f32)
            85..86 'a': u8
            92..93 'c': f32
            98..100 '{}': ()
            110..120 '(a, b, ..)': (u8, i16, f32)
            111..112 'a': u8
            114..115 'b': i16
            124..126 '{}': ()
            136..142 '(a, b)': (u8, i16)
            137..138 'a': u8
            140..141 'b': i16
            146..161 '{/*too short*/}': ()
            170..182 '(a, b, c, d)': (u8, i16, f32, {unknown})
            171..172 'a': u8
            174..175 'b': i16
            177..178 'c': f32
            180..181 'd': {unknown}
            186..200 '{/*too long*/}': ()
            209..210 '_': (u8, i16, f32)
            214..216 '{}': ()
        "#]],
    );
}

#[test]
fn tuple_struct_ellipsis_pattern() {
    check_infer(
        r#"
struct Tuple(u8, i16, f32);
fn foo(tuple: Tuple) {
    match tuple {
        Tuple(.., b, c) => {},
        Tuple(a, .., c) => {},
        Tuple(a, b, ..) => {},
        Tuple(a, b) => {/*too short*/}
        Tuple(a, b, c, d) => {/*too long*/}
        _ => {}
    }
}"#,
        expect![[r#"
            35..40 'tuple': Tuple
            49..268 '{     ...   } }': ()
            55..266 'match ...     }': ()
            61..66 'tuple': Tuple
            77..92 'Tuple(.., b, c)': Tuple
            87..88 'b': i16
            90..91 'c': f32
            96..98 '{}': ()
            108..123 'Tuple(a, .., c)': Tuple
            114..115 'a': u8
            121..122 'c': f32
            127..129 '{}': ()
            139..154 'Tuple(a, b, ..)': Tuple
            145..146 'a': u8
            148..149 'b': i16
            158..160 '{}': ()
            170..181 'Tuple(a, b)': Tuple
            176..177 'a': u8
            179..180 'b': i16
            185..200 '{/*too short*/}': ()
            209..226 'Tuple(... c, d)': Tuple
            215..216 'a': u8
            218..219 'b': i16
            221..222 'c': f32
            224..225 'd': {unknown}
            230..244 '{/*too long*/}': ()
            253..254 '_': Tuple
            258..260 '{}': ()
        "#]],
    );
}

#[test]
fn const_block_pattern() {
    check_infer(
        r#"
struct Foo(usize);
fn foo(foo: Foo) {
    match foo {
        const { Foo(15 + 32) } => {},
        _ => {}
    }
}"#,
        expect![[r#"
            26..29 'foo': Foo
            36..115 '{     ...   } }': ()
            42..113 'match ...     }': ()
            48..51 'foo': Foo
            62..84 'const ... 32) }': Foo
            68..84 '{ Foo(... 32) }': Foo
            70..73 'Foo': Foo(usize) -> Foo
            70..82 'Foo(15 + 32)': Foo
            74..76 '15': usize
            74..81 '15 + 32': usize
            79..81 '32': usize
            88..90 '{}': ()
            100..101 '_': Foo
            105..107 '{}': ()
        "#]],
    );
}

#[test]
fn macro_pat() {
    check_types(
        r#"
macro_rules! pat {
    ($name:ident) => { Enum::Variant1($name) }
}

enum Enum {
    Variant1(u8),
    Variant2,
}

fn f(e: Enum) {
    match e {
        pat!(bind) => {
            bind;
          //^^^^ u8
        }
        Enum::Variant2 => {}
    }
}
    "#,
    )
}
